﻿using System;
using System.IO;
using System.Collections.Generic;


namespace FileSplitMerge
{

    internal static class Utils
    {
        public delegate void Output(long progress);

        internal static void FileSplit(string pathInput, int chunkSize, string dir_path, Output outPutProgress)
        {
            string dir = Path.GetDirectoryName(pathInput);
            string fileName = Path.GetFileNameWithoutExtension(pathInput);
            string ext = Path.GetExtension(pathInput);
            byte[] buffer = new byte[chunkSize];

            FileInfo input = new FileInfo(pathInput);
            using (FileStream fsIn = input.OpenRead())
            {
                int index = 0;
                while (fsIn.Position < fsIn.Length)
                {
                    string pathOut = Path.Combine(dir_path, fileName) + index.ToString() + ext;
                    using (FileStream fsOut = File.Open(pathOut, FileMode.OpenOrCreate))
                    {
                        int chunkByteRead = fsIn.Read(buffer, 0, chunkSize);
                        fsOut.Write(buffer, 0, chunkByteRead);
                    }
                    index++;
                   long curPos = CalcProgress(fsIn.Length, fsIn.Position);
                   outPutProgress(curPos);
                }
            }
        }

        internal static void FileMerge(string dirPath, Output outPutProgress)
        {
            DirectoryInfo di = new DirectoryInfo(dirPath);
            FileInfo[] chunkOfFile = di.GetFiles();
            string destFileName = Path.GetFileNameWithoutExtension(chunkOfFile[0].FullName);
            destFileName = destFileName.Remove(destFileName.Length - 1);
            string dir = Path.GetDirectoryName(chunkOfFile[0].FullName);
            string ext = Path.GetExtension(chunkOfFile[0].FullName);
            FileInfo fileOut = new FileInfo(Path.Combine(dir, destFileName) + ext);

            using (FileStream output = fileOut.Open(FileMode.OpenOrCreate, FileAccess.Write, FileShare.Write))
            {
                long chunks = 0;
                foreach (FileInfo chunk in chunkOfFile)
                {
                    chunks++;
                    int count;
                    int bufferSize = 32 * 1024 * 1024;
                    byte[] data = new byte[bufferSize];
                    using (FileStream input = chunk.OpenRead())
                    {
                        while ( (count = input.Read(data, 0, data.Length)) > 0)
                        {
                            output.Write(data, 0, count);
                        }
                    }

                    long curPos = CalcProgress(chunkOfFile.Length, chunks);
                    outPutProgress(curPos);
                }
            }
        }

        static List<DirFileInf> AbsPathDirectoryCreate(string rootPath)
        {
            DirectoryInfo mainDir = new DirectoryInfo(rootPath);
            DirectoryInfo[] subDirs = mainDir.GetDirectories();
            FileInfo[] subFiles = mainDir.GetFiles();
            List<DirFileInf> dirProps = new List<DirFileInf>();
            byte[] buffer = new byte[1024];

            foreach (FileInfo subFile in subFiles)
            {

                DirFileInf filePath = new DirFileInf(subFile.FullName);
                filePath.FileSize = subFile.Length;
                dirProps.Add(filePath);
            }

            foreach (DirectoryInfo subDir in subDirs)
            {
                DirFileInf dirPath = new DirFileInf(subDir.FullName);
                dirProps.Add(dirPath);
                dirPath.IsDir = true;
                List<DirFileInf> dir = AbsPathDirectoryCreate(subDir.FullName);
                dirProps.AddRange(dir);
            }

            return dirProps; 
        }

        static List<DirFileInf> RelPathDirectoryCreate(string rootPath)
        {
            DirectoryInfo mainDir = new DirectoryInfo(rootPath);
            DirectoryInfo[] subDirs = mainDir.GetDirectories();
            List<DirFileInf> dirProps = AbsPathDirectoryCreate(rootPath);

            int subStr = mainDir.FullName.Length - mainDir.Name.Length;

            foreach (DirFileInf dirProp in dirProps)
            {
                dirProp.Path = dirProp.FullPath.Substring(subStr);
            }
            
            return dirProps; 

        }

        internal static void Zip(string rootPath, string destPath, Output outPutProgress)
        {
            List<DirFileInf> dirProps = RelPathDirectoryCreate(rootPath);
            byte[] buffer = new byte[32*1024];
            int count;
            
            using ( BinaryWriter writer = new BinaryWriter(File.Open(destPath, FileMode.Create)) )
            {   
				writer.Write(dirProps.Count);

                long dirPropsLCount = 0;
                foreach (DirFileInf dirProp in dirProps)
                {
                    dirPropsLCount++;
					writer.Write(dirProp.Path);
                    writer.Write(dirProp.IsDir);

                    if (!dirProp.IsDir)
                    {
                        writer.Write(dirProp.FileSize);
                        FileInfo file = new FileInfo(dirProp.FullPath);
                        using ( FileStream stream = file.OpenRead())
                        {
                            while ( (count = stream.Read(buffer, 0, buffer.Length)) > 0)
                            {
                                writer.Write(buffer,0,count);
                            }
                        }
                    }

                    long curPos = CalcProgress((long)dirProps.Count, dirPropsLCount);
                    outPutProgress(curPos);
                }
            }
        }

        internal static void Unzip (string filePath, string destPath, Output outPutProgress)
        {
            int elementCount = 0;
			byte[] buffer = new byte[32*1024];
			//byte temp;
            string rootDirName = Path.GetFileNameWithoutExtension(filePath);
            string rootDirFullName = Path.Combine(destPath, rootDirName);
            DirectoryInfo rootDir = new DirectoryInfo(rootDirFullName);
            rootDir.Create();
			
			using (BinaryReader reader = new BinaryReader(File.Open(filePath, FileMode.OpenOrCreate)))
			{
				elementCount = reader.ReadInt32();
				DirFileInf[] element = new DirFileInf[elementCount];
				for (int i = 0 ; i < elementCount; i++)
				{
                    string readPath = reader.ReadString();
					string fullPath= Path.Combine(destPath, readPath);					
					element[i] = new DirFileInf(fullPath);
					element[i].IsDir = reader.ReadBoolean();
					
					
					if (element[i].IsDir)
					{
						DirectoryInfo newDir = new DirectoryInfo(fullPath);
						newDir.Create();
					}
					else 
					{
						using (BinaryWriter writer = new BinaryWriter(File.Open(fullPath, FileMode.OpenOrCreate)) )
						{
							element[i].FileSize = reader.ReadInt64();
                            writer.Write(reader.ReadBytes((int)element[i].FileSize));
        				}
					}
                    long curPos = CalcProgress(elementCount, i+1);
                    outPutProgress(curPos);
	    		}
			}
		}

        internal static long CalcProgress(long length, long position)
        {
            long progress;
            return progress = position*100/length;
        }
     }
}
